/**
 * 
 */
package com.dalonedrow.engine.sprite.base;

/**
 * @author Donald
 */
public final class SimpleColor {
	/**
	 * The color white. In the default sRGB space.
	 */
	public static final SimpleColor	white		= new SimpleColor(255, 255, 255);
	/**
	 * The color white. In the default sRGB space.
	 * @since 1.4
	 */
	public static final SimpleColor	WHITE		= SimpleColor.white;
	/**
	 * The alpha value as a <code>float</code> component. If
	 * <code>frgbvalue</code> is <code>null</code>, this is not valid data, so
	 * compute from the <code>int</code> color value.
	 */
	private float					falpha		= 0.0f;
	/**
	 * The color value in the default sRGB <code>ColorSpace</code> as
	 * <code>float</code> components (no alpha). If <code>null</code> after
	 * object construction, this must be an sRGB color constructed with 8-bit
	 * precision, so compute from the <code>int</code> color value.
	 */
	private float[]					frgbvalue	= null;
	/**
	 * The color value in the native <code>ColorSpace</code> as
	 * <code>float</code> components (no alpha). If <code>null</code> after
	 * object construction, this must be an sRGB color constructed with 8-bit
	 * precision, so compute from the <code>int</code> color value.
	 */
	private float[]					fvalue		= null;
	/**
	 * The color value.
	 */
	private int						value;
	/**
	 * Creates an opaque sRGB color with the specified red, green, and blue
	 * values in the range (0.0 - 1.0). Alpha is defaulted to 1.0. The actual
	 * color used in rendering depends on finding the best match given the color
	 * space available for a particular output device.
	 * @throws IllegalArgumentException if <code>r</code>, <code>g</code> or
	 *         <code>b</code> are outside of the range 0.0 to 1.0, inclusive
	 * @param r the red component
	 * @param g the green component
	 * @param b the blue component
	 */
	public SimpleColor(float r, float g, float b) {
		this((int) (r * 255 + 0.5), (int) (g * 255 + 0.5),
				(int) (b * 255 + 0.5));
		testColorValueRange(r, g, b, 1.0f);
		frgbvalue = new float[3];
		frgbvalue[0] = r;
		frgbvalue[1] = g;
		frgbvalue[2] = b;
		falpha = 1.0f;
		fvalue = frgbvalue;
	}
	/**
	 * Creates an sRGB color with the specified red, green, blue, and alpha
	 * values in the range (0.0 - 1.0). The actual color used in rendering
	 * depends on finding the best match given the color space available for a
	 * particular output device.
	 * @throws IllegalArgumentException if <code>r</code>, <code>g</code>
	 *         <code>b</code> or <code>a</code> are outside of the range 0.0 to
	 *         1.0, inclusive
	 * @param r the red component
	 * @param g the green component
	 * @param b the blue component
	 * @param a the alpha component
	 */
	public SimpleColor(float r, float g, float b, float a) {
		this((int) (r * 255 + 0.5), (int) (g * 255 + 0.5),
				(int) (b * 255 + 0.5), (int) (a * 255 + 0.5));
		frgbvalue = new float[3];
		frgbvalue[0] = r;
		frgbvalue[1] = g;
		frgbvalue[2] = b;
		falpha = a;
		fvalue = frgbvalue;
	}
	/**
	 * Creates an opaque sRGB color with the specified combined RGB value
	 * consisting of the red component in bits 16-23, the green component in
	 * bits 8-15, and the blue component in bits 0-7. The actual color used in
	 * rendering depends on finding the best match given the color space
	 * available for a particular output device. Alpha is defaulted to 255.
	 * @param rgb the combined RGB components
	 */
	public SimpleColor(final int rgb) {
		value = 0xff000000 | rgb;
	}
	/**
	 * Creates an sRGB color with the specified combined RGBA value consisting
	 * of the alpha component in bits 24-31, the red component in bits 16-23,
	 * the green component in bits 8-15, and the blue component in bits 0-7. If
	 * the <code>hasalpha</code> argument is <code>false</code>, alpha is
	 * defaulted to 255.
	 * @param rgba the combined RGBA components
	 * @param hasalpha <code>true</code> if the alpha bits are valid;
	 *        <code>false</code> otherwise
	 */
	public SimpleColor(int rgba, boolean hasalpha) {
		if (hasalpha) {
			value = rgba;
		} else {
			value = 0xff000000 | rgba;
		}
	}
	/**
	 * Creates an opaque sRGB color with the specified red, green, and blue
	 * values in the range (0 - 255). The actual color used in rendering depends
	 * on finding the best match given the color space available for a given
	 * output device. Alpha is defaulted to 255.
	 * @throws IllegalArgumentException if <code>r</code>, <code>g</code> or
	 *         <code>b</code> are outside of the range 0 to 255, inclusive
	 * @param r the red component
	 * @param g the green component
	 * @param b the blue component
	 */
	public SimpleColor(int r, int g, int b) {
		this(r, g, b, 255);
	}
	/**
	 * Creates an sRGB color with the specified red, green, blue, and alpha
	 * values in the range (0 - 255).
	 * @throws IllegalArgumentException if <code>r</code>, <code>g</code>,
	 *         <code>b</code> or <code>a</code> are outside of the range 0 to
	 *         255, inclusive
	 * @param r the red component
	 * @param g the green component
	 * @param b the blue component
	 * @param a the alpha component
	 */
	public SimpleColor(int r, int g, int b, int a) {
		value = (a & 0xFF) << 24 |
				(r & 0xFF) << 16 |
				(g & 0xFF) << 8 |
				(b & 0xFF) << 0;
		testColorValueRange(r, g, b, a);
	}
	/**
	 * Returns the alpha component in the range 0-255.
	 * @return the alpha component.
	 */
	public int getAlpha() {
		return getRGB() >> 24 & 0xff;
	}
	/**
	 * Returns the blue component in the range 0-255 in the default sRGB space.
	 * @return the blue component.
	 */
	public int getBlue() {
		return getRGB() >> 0 & 0xFF;
	}
	/**
	 * Returns a <code>float</code> array containing only the color components
	 * of the <code>Color</code>, in the <code>ColorSpace</code> of the
	 * <code>Color</code>. If <code>compArray</code> is <code>null</code>, an
	 * array with length equal to the number of components in the associated
	 * <code>ColorSpace</code> is created for the return value. Otherwise,
	 * <code>compArray</code> must have at least this length and it is filled in
	 * with the components and returned.
	 * @param compArray an array that this method fills with the color
	 *        components of this <code>Color</code> in its
	 *        <code>ColorSpace</code> and returns
	 * @return the color components in a <code>float</code> array.
	 */
	public float[] getColorComponents(float[] compArray) {
		if (fvalue == null) {
			return getRGBColorComponents(compArray);
		}
		float[] f;
		int n = fvalue.length;
		if (compArray == null) {
			f = new float[n];
		} else {
			f = compArray;
		}
		for (int i = 0; i < n; i++) {
			f[i] = fvalue[i];
		}
		return f;
	}
	/**
	 * Returns a <code>float</code> array containing the color and alpha
	 * components of the <code>Color</code>, in the <code>ColorSpace</code> of
	 * the <code>Color</code>. If <code>compArray</code> is <code>null</code>,
	 * an array with length equal to the number of components in the associated
	 * <code>ColorSpace</code> plus one is created for the return value.
	 * Otherwise, <code>compArray</code> must have at least this length and it
	 * is filled in with the components and returned.
	 * @param compArray an array that this method fills with the color and alpha
	 *        components of this <code>Color</code> in its
	 *        <code>ColorSpace</code> and returns
	 * @return the color and alpha components in a <code>float</code> array.
	 */
	public float[] getComponents(final float[] compArray) {
		if (fvalue == null) {
			return getRGBComponents(compArray);
		}
		float[] f;
		int n = fvalue.length;
		if (compArray == null) {
			f = new float[n + 1];
		} else {
			f = compArray;
		}
		for (int i = 0; i < n; i++) {
			f[i] = fvalue[i];
		}
		f[n] = falpha;
		return f;
	}
	/**
	 * Returns the green component in the range 0-255 in the default sRGB space.
	 * @return the green component.
	 */
	public int getGreen() {
		return getRGB() >> 8 & 0xFF;
	}
	/**
	 * Returns the red component in the range 0-255 in the default sRGB space.
	 * @return the red component.
	 */
	public int getRed() {
		return getRGB() >> 16 & 0xFF;
	}
	/**
	 * Returns the RGB value representing the color in the default sRGB. 
	 * (Bits 24-31 are alpha, 16-23 are red, 8-15 are green,
	 * 0-7 are blue).
	 * @return the RGB value of the color in the default sRGB
	 *         <code>ColorModel</code>.
	 */
	public int getRGB() {
		return value;
	}
	/**
	 * Returns a <code>float</code> array containing only the color components
	 * of the <code>Color</code>, in the default sRGB color space. If
	 * <code>compArray</code> is <code>null</code>, an array of length 3 is
	 * created for the return value. Otherwise, <code>compArray</code> must have
	 * length 3 or greater, and it is filled in with the components and
	 * returned.
	 * @param compArray an array that this method fills with color components
	 *        and returns
	 * @return the RGB components in a <code>float</code> array.
	 */
	public float[] getRGBColorComponents(float[] compArray) {
		float[] f;
		if (compArray == null) {
			f = new float[3];
		} else {
			f = compArray;
		}
		if (frgbvalue == null) {
			f[0] = getRed() / 255f;
			f[1] = getGreen() / 255f;
			f[2] = getBlue() / 255f;
		} else {
			f[0] = frgbvalue[0];
			f[1] = frgbvalue[1];
			f[2] = frgbvalue[2];
		}
		return f;
	}
	/**
	 * Returns a <code>float</code> array containing the color and alpha
	 * components of the <code>Color</code>, as represented in the default sRGB
	 * color space. If <code>compArray</code> is <code>null</code>, an array of
	 * length 4 is created for the return value. Otherwise,
	 * <code>compArray</code> must have length 4 or greater, and it is filled in
	 * with the components and returned.
	 * @param compArray an array that this method fills with color and alpha
	 *        components and returns
	 * @return the RGBA components in a <code>float</code> array.
	 */
	public float[] getRGBComponents(float[] compArray) {
		float[] f;
		if (compArray == null) {
			f = new float[4];
		} else {
			f = compArray;
		}
		if (frgbvalue == null) {
			f[0] = getRed() / 255f;
			f[1] = getGreen() / 255f;
			f[2] = getBlue() / 255f;
			f[3] = getAlpha() / 255f;
		} else {
			f[0] = frgbvalue[0];
			f[1] = frgbvalue[1];
			f[2] = frgbvalue[2];
			f[3] = falpha;
		}
		return f;
	}
	/**
	 * Checks the color <code>float</code> components supplied for validity.
	 * Throws an <code>IllegalArgumentException</code> if the value is out of
	 * range.
	 * @param r the Red component
	 * @param g the Green component
	 * @param b the Blue component
	 **/
	private void testColorValueRange(float r, float g, float b, float a) {
		boolean rangeError = false;
		String badComponentString = "";
		if (a < 0.0 || a > 1.0) {
			rangeError = true;
			badComponentString = badComponentString + " Alpha";
		}
		if (r < 0.0 || r > 1.0) {
			rangeError = true;
			badComponentString = badComponentString + " Red";
		}
		if (g < 0.0 || g > 1.0) {
			rangeError = true;
			badComponentString = badComponentString + " Green";
		}
		if (b < 0.0 || b > 1.0) {
			rangeError = true;
			badComponentString = badComponentString + " Blue";
		}
		if (rangeError == true) {
			throw new IllegalArgumentException(
					"Color parameter outside of expected range:"
							+ badComponentString);
		}
	}
	/**
	 * Checks the color integer components supplied for validity. Throws an
	 * {@link IllegalArgumentException} if the value is out of range.
	 * @param r the Red component
	 * @param g the Green component
	 * @param b the Blue component
	 **/
	private void testColorValueRange(int r, int g, int b, int a) {
		boolean rangeError = false;
		String badComponentString = "";

		if (a < 0 || a > 255) {
			rangeError = true;
			badComponentString = badComponentString + " Alpha";
		}
		if (r < 0 || r > 255) {
			rangeError = true;
			badComponentString = badComponentString + " Red";
		}
		if (g < 0 || g > 255) {
			rangeError = true;
			badComponentString = badComponentString + " Green";
		}
		if (b < 0 || b > 255) {
			rangeError = true;
			badComponentString = badComponentString + " Blue";
		}
		if (rangeError == true) {
			throw new IllegalArgumentException(
					"Color parameter outside of expected range:"
							+ badComponentString);
		}
	}
	/**
	 * Returns a string representation of this <code>Color</code>. This method
	 * is intended to be used only for debugging purposes. The content and
	 * format of the returned string might vary between implementations. The
	 * returned string might be empty but cannot be <code>null</code>.
	 * @return a string representation of this <code>Color</code>.
	 */
	@Override
	public String toString() {
		return getClass().getName() + "[r=" + getRed() + ",g=" + getGreen()
				+ ",b=" + getBlue() + "]";
	}
}
